package org.jeecg.modules.dzswj;


import com.ximpleware.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;


public class VtdXmlOperateDelegate {
	private static final Logger log = LoggerFactory
			.getLogger(VtdXmlOperateDelegate.class);
	private VTDNav vtdGenNav;

	public VtdXmlOperateDelegate(String xml) {
		this.vtdGenNav = VtdXmlOperateUtil.generateVtdGenNav(xml);
	}

	public Object selectByXpath(String xpath) {
		return VtdXmlOperateUtil.selectByXpath(this.vtdGenNav, xpath);
	}

	public Object selectByXpath(String xpath, boolean IsWrappeTag) {
		return VtdXmlOperateUtil.selectNodeTextByXpath(this.vtdGenNav, xpath,
				IsWrappeTag);
	}

	public boolean moveCursorToByXpath(String xpath) {
		return VtdXmlOperateUtil.moveCursorToByXpath(this.vtdGenNav, xpath);
	}

	public String getNodeDataByXpath(String xpath) {
		try {
			Object object = selectByXpath(xpath);
			if (object == null) {
				object = "";
			} else if (!(object instanceof String)) {
				log.error("请求报文错误，错误原因：存在多个"
						+ xpath
						+ "路径，请求报文为："
						+ new String(this.vtdGenNav.getXML().getBytes(),
								"UTF-8"));

				throw new Exception("请求报文错误！");
			}

			return ((String) object);
		} catch (Exception e) {
			try {
				log.error("请求报文结构错误，错误报文为："
						+ new String(this.vtdGenNav.getXML().getBytes(),
								"UTF-8"));
			} catch (UnsupportedEncodingException e1) {
				log.error("字符编码转换异常：", e1);
				e1.printStackTrace();
			}
			log.error("解析错误",e);
			e.printStackTrace();
		}
		return null;
	}

	public int getCursorPosition() {
		return this.vtdGenNav.getCurrentIndex();
	}

	public void recoverToPosition(int position) {
		VtdXmlOperateUtil.recoverToPosition(this.vtdGenNav, position);
	}

	public String getCurrentNodeText() {
		return VtdXmlOperateUtil.getNodeText(this.vtdGenNav, false);
	}

	public String getCurrentNodeAttrible(String attribleName,
			boolean keepPosition) {
		return VtdXmlOperateUtil.getNodeAttribue(this.vtdGenNav, attribleName,
				keepPosition);
	}

	public boolean moveCursorTo(int direction, String element) {
		return VtdXmlOperateUtil.moveCursorTo(this.vtdGenNav, direction,
				element);
	}

	public String moveCursorToAndGetText(int direction, String element) {
		return VtdXmlOperateUtil.moveCursorToAndGetText(this.vtdGenNav,
				direction, element);
	}

	public static class VtdXmlOperateUtil {
		private static final Logger log = LoggerFactory
				.getLogger(VtdXmlOperateUtil.class);
		public static final int NEXT_SIBLING = 4;
		public static final int PREV_SIBLING = 5;
		public static final int PAREN = 1;
		public static final int ROOT = 0;
		public static final int FIRST_CHILD = 2;
		public static final int LAST_CHILD = 3;
		public static final int BROTHER = 6;
		public static final int SELF_OR_BROTHER = 7;

		/**
		 * 根据xml获得对应的VTDNav
		 * @param xml
		 * @return
		 * @throws Exception 
		 */
		public static VTDNav getVTDNav(String xml) throws Exception {
			VTDGen gen = new VTDGen();
			VTDNav nav = null;
			if (xml == null || xml.equals("")) {
				return null;
			}
			byte[] bs;
			try {
				bs = xml.getBytes("UTF-8");
			} catch (UnsupportedEncodingException e1) {
				log.error("xml encoding is not utf-8！");
				throw e1;
			}
			gen.setDoc(bs);
			try {
				gen.parse(false);
				nav = gen.getNav();
			} catch (Exception e) {
				throw e;
			}
			return nav;
		}

		public static Object selectByXpath(String xml, String xpath) throws Exception {
			VTDGen tgen = new VTDGen();
			if ((xml == null) || (xml.equals("")))
				return null;
			byte[] bs;
			try {
				bs = xml.getBytes("UTF-8");
			} catch (UnsupportedEncodingException e1) {
				throw new Exception("xml encoding is not utf-8！");
			}
			tgen.setDoc(bs);

			return selectByXpath(tgen.getNav(), xpath);
		}

		public static Object selectByXpath(VTDNav genNav, String xpath) {
			return selectNodeTextByXpath(genNav, xpath, true);
		}

		public static Object selectNodeTextByXpath(VTDNav genNav, String xpath,
				boolean IsWrappeTag) {
			if (genNav == null) {
				return null;
			}

			List lst = new ArrayList();
			try {
				AutoPilot autoPilot = new AutoPilot(genNav);
				autoPilot.selectXPath(xpath);
				int pos = -1;
				pos = autoPilot.evalXPath();
				while (pos > 0) {
					VtdSelectResultWrapper wrapper = new VtdSelectResultWrapper();
					wrapper.setNodeName(genNav.toString(pos));
					wrapper.setText(getNodeText(genNav, IsWrappeTag));
					if (genNav.getText() < 0) {
						if (genNav.getContentFragment() >= 0L)
							wrapper.setXmlNode(true);
						else {
							wrapper.setText("");
						}
					}
					lst.add(wrapper);
					pos = autoPilot.evalXPath();
				}
			} catch (Exception e) {
				log.error("解析错误",e);
				e.printStackTrace();
			}
			if ((lst.size() == 1)
					&& (!(((VtdSelectResultWrapper) lst.get(0)).isXmlNode())))
				return ((VtdSelectResultWrapper) lst.get(0)).getText();
			if (lst.size() == 0) {
				return null;
			}
			return lst;
		}

		/**
		 * 返回根据xpath表达式选择的xml节点信息。
		 * 如果值查询结果只有一个节点,且节点值不为子节点,返回String类型.其他返回
		 * List<VtdSelectResultWrapper>.
		 * 此方法结合getVTDNav方法，替代selectByXpath，用来提升VTDXml的效率，避免多次的VTDGen操作
		 * 如果需要从1个xml中多次读取数据，请用此方法
		 * @param nav
		 * @param xpath
		 * @return
		 * @throws Exception 
		 */
		public static Object selectByXpathWithVTDNav(VTDNav nav, String xpath) throws Exception {
			List<VtdSelectResultWrapper> lst = new ArrayList<VtdSelectResultWrapper>();
			try {
				AutoPilot autoPilot = new AutoPilot(nav);
				autoPilot.selectXPath(xpath);
				int pos = -1;
				pos = autoPilot.evalXPath();
				while (pos > 0) {
					VtdSelectResultWrapper wrapper = new VtdSelectResultWrapper();
					wrapper.setNodeName(nav.toString(pos));
					wrapper.setText(getNodeText(nav,true));
					if (nav.getText() < 0){
						wrapper.setXmlNode(true);
					}
					lst.add(wrapper);
					pos = autoPilot.evalXPath();
				}
			} catch (Exception e) {
				throw e;
			}
			if ((lst.size() == 1) && !lst.get(0).isXmlNode()){
				return lst.get(0).getText();
			}
			return lst;
		}
		
		public static boolean moveCursorToByXpath(VTDNav genNav, String xpath) {
			if (genNav == null)
				return false;
			try {
				AutoPilot autoPilot = new AutoPilot(genNav);
				autoPilot.selectXPath(xpath);
				int pos = -1;
				pos = autoPilot.evalXPath();
				if (pos < 0)
					return false;
			} catch (Exception e) {
				log.error("解析错误",e);
				e.printStackTrace();
			}
			return true;
		}

		public static String getNodeText(VTDNav nav, boolean IsWrappeTag) {
			try {
				int pos = nav.getText();
				if (pos < 0) {
					long posInf = -1L;
					if (IsWrappeTag) {
						posInf = nav.getElementFragment();
					} else {
						posInf = nav.getContentFragment();
						if ((posInf < 0L) && (nav.getElementFragment() >= 0L)) {
							return "";
						}
					}
					return nav.toRawString((int) posInf, (int) (posInf >> 32));
				}
				return nav.toString(pos);
			} catch (Exception e) {
				log.error("解析错误",e);
				e.printStackTrace();
			}
			return null;
		}

		public static String getNodeAttribue(VTDNav nav, String attribleName,
				boolean keepPosition) {
			String str = "";	
			int curPos = nav.getCurrentIndex();
			try {
				int pos = nav.getAttrVal(attribleName);
				if (pos > 0) {
					str = nav.toString(pos);
					return str;
				}
			} catch (Exception e) {
			} finally {
				try {
					if (keepPosition)
						nav.recoverNode(curPos);
				} catch (NavException e) {
				}
			}
			return str;
		}

		public static String getNodeDataByXpath(String requestXml, String xpath) {
			try {
				Object object = selectByXpath(requestXml, xpath);
				if (object == null) {
					object = "";
				} else if (!(object instanceof String)) {
					log.error("请求报文错误，错误原因：存在多个" + xpath + "路径，请求报文为："
							+ requestXml);

					throw new Exception("请求报文错误！");
				}
				return ((String) object);
			} catch (Exception e) {
				log.error("请求报文结构错误，错误报文为：" + requestXml);
				e.printStackTrace();
			}
			return null;
		}

		/**
		 * @param inputXml
		 * @param insertNote
		 * @throws UnsupportedEncodingException 
		 * @function 往xml指定节点之前中插入数据，并返回操作完成后的xml文件
		 * 
		 */
		public static String insertNoteToInputXml(String inputXml,String insertNote,String insertPath) throws UnsupportedEncodingException{
			String xml="";
			VTDGen gen = new VTDGen();
			if (inputXml == null || inputXml.equals("")) {
				return null;
			}
			byte[] bs;
			try {
				bs = inputXml.getBytes("UTF-8");
			} catch (UnsupportedEncodingException e1) {
				log.error("xml encoding is not utf-8！");
				throw e1;
			}
			try{
			ByteArrayOutputStream bao=new ByteArrayOutputStream();
			gen.setDoc(bs);
			 int i;
	         AutoPilot ap = new AutoPilot();
			  ap.selectXPath(insertPath);
	         XMLModifier xm = new XMLModifier();
	         gen.parse(false);
	             VTDNav vn = gen.getNav();
	             ap.bind(vn);
	             xm.bind(vn);
	             while ((i = ap.evalXPath()) != -1)
	             {
	                 xm.insertBeforeElement(insertNote);
	             }
	             xm.output(bao);
	             xml = new String(bao.toByteArray(), "UTF-8");
			     bao.close();
			     return xml;
			}catch(Exception e){
				return "";
			}
		}
		
		public static boolean moveCursorTo(VTDNav nav, int direction,
				String element) {
			try {
				if ((direction == 6) || (direction == 7)) {
					if ((direction == 7)
							&& (nav.toString(nav.getCurrentIndex())
									.equals(element))) {
						return true;
					}
					boolean flag = nav.toElement(4, element);
					if (!(flag)) {
						flag = nav.toElement(5, element);
					}
					return flag;
				}
				return nav.toElement(direction, element);
			} catch (NavException e) {
				log.error("解析错误",e);
				e.printStackTrace();
			}
			return false;
		}

		public static String moveCursorToAndGetText(VTDNav nav, int direction,
				String element) {
			if (moveCursorTo(nav, direction, element)) {
				return getNodeText(nav, false);
			}
			return null;
		}

		/**
		 * @param inputXml
		 * @param insertNote
		 * @throws UnsupportedEncodingException 
		 * @function 删除指定节点，并返回操作完成后的xml文件
		 */
		public static String removeNoteToInputXml(String inputXml,String insertNote,String insertPath) throws UnsupportedEncodingException{
			String xml="";
			VTDGen gen = new VTDGen();
			if (inputXml == null || inputXml.equals("")) {
				return null;
			}
			byte[] bs;
			try {
				bs = inputXml.getBytes("UTF-8");
			} catch (UnsupportedEncodingException e1) {
				log.error("xml encoding is not utf-8！");
				throw e1;
			}
			try{
			ByteArrayOutputStream bao=new ByteArrayOutputStream();
			gen.setDoc(bs);
			 int i=-1;
	          AutoPilot ap = new AutoPilot();
			  ap.selectXPath(insertPath);
	         XMLModifier xm = new XMLModifier();
	          gen.parse(false);
	             VTDNav vn = gen.getNav();
	             ap.bind(vn);
	             xm.bind(vn);
	             while ((i = ap.evalXPath()) != -1)
	             {
	                 xm.remove();  
	             }
	             xm.output(bao);
	             xml = new String(bao.toByteArray(), "UTF-8");
			     bao.close();
			     return xml;
			}catch(Exception e){
				return "";
			}
		}
		
		public static void recoverToPosition(VTDNav nav, int position) {
			try {
				nav.recoverNode(position);
			} catch (NavException e) {
				log.error("错误",e);
				e.printStackTrace();
			}
		}

		public static VTDNav generateVtdGenNav(String xml) {
			VTDGen gen = new VTDGen();
			if ((xml == null) || (xml.equals("")))
				return null;
			byte[] bs = null;
			try {
				bs = xml.getBytes("UTF-8");
			} catch (UnsupportedEncodingException e1) {
				log.error("xml encoding is not utf-8！");
				e1.printStackTrace();
			}
			gen.setDoc(bs);
			try {
				gen.parse(false);
			} catch (Exception e) {
				log.error("报文解析异常");
				e.printStackTrace();
			}
			return gen.getNav();
		}
		
		/**
		 * @param inputXml
		 * @param insertNote
		 * @throws UnsupportedEncodingException 
		 * @function 在指定节点中增加xml，并返回
		 */
		public static String updateNode(String inputXml,String insertNote,String insertPath) throws UnsupportedEncodingException{
			inputXml = createXpath(inputXml,insertPath);
			String xml="";
			VTDGen gen = new VTDGen();
			if (inputXml == null || inputXml.equals("")) {
				return null;
			}
			byte[] bs;
			try {
				bs = inputXml.getBytes("UTF-8");
			} catch (UnsupportedEncodingException e1) {
				log.error("xml encoding is not utf-8！");
				throw e1;
			}
			try{
			ByteArrayOutputStream bao=new ByteArrayOutputStream();
			
			gen.setDoc(bs);
			 int i;
	         AutoPilot ap = new AutoPilot();
			  ap.selectXPath(insertPath);
	         XMLModifier xm = new XMLModifier();
	         gen.parse(false);
	             VTDNav vn = gen.getNav();
	             ap.bind(vn);
	             xm.bind(vn);
	             while ((i = ap.evalXPath()) != -1)
	             {
//	            	 在内容中插入
	                 xm.insertAfterHead(insertNote); 
//	                 节点标签中插入
//	                 xm.insertAttribute(insertNote);
//	            	 替换标签
//	                 xm.updateElementName(insertNote);
	             }
	             xm.output(bao);
	             xml = new String(bao.toByteArray(), "UTF-8");
			     bao.close();
			     return xml;
			}catch(Exception e){
				return "";
			}
		}
		
		/**
		 * @param inputXml
		 * @param insertNote
		 * @throws UnsupportedEncodingException 
		 * @function 修改指定节点里的数据内容，并返回操作完成后的xml文件
		 */
		public static String updateNoteToInputXml(String inputXml,String insertNote,String insertPath) throws UnsupportedEncodingException{
			inputXml = createXpath(inputXml,insertPath);
			String xml="";
			VTDGen gen = new VTDGen();
			if (inputXml == null || inputXml.equals("")) {
				return null;
			}
			byte[] bs;
			try {
				bs = inputXml.getBytes("UTF-8");
			} catch (UnsupportedEncodingException e1) {
				log.error("xml encoding is not utf-8！");
				throw e1;
			}
			try{
			ByteArrayOutputStream bao=new ByteArrayOutputStream();
			gen.setDoc(bs);
			 int i=-1;
	          AutoPilot ap = new AutoPilot();
			  ap.selectXPath(insertPath+"/text()");
	         XMLModifier xm = new XMLModifier();
	          gen.parse(false);
	             VTDNav vn = gen.getNav();
	             ap.bind(vn);
	             xm.bind(vn);
	             while ((i = ap.evalXPath()) != -1)
	             {
	                 xm.updateToken(i,insertNote);  
	             }
	             xm.output(bao);
	             xml = new String(bao.toByteArray(), "UTF-8");
			     bao.close();
			     return xml;
			}catch(Exception e){
				return "";
			}
		}
		
		/**
		 * 创建xpath
		 * @param inputXml
		 * @param xPath
		 * @return
		 * @throws UnsupportedEncodingException
		 */
		public static String createXpath(String inputXml,String xPath) throws UnsupportedEncodingException{
	        String[] xPaths = xPath.split("/");
	        xPath = "";
	        for(String path:xPaths){
	      	  if(path==null||path.equals("")){continue;}
	      	  xPath += "/"+path;
		      	String xml="";
				VTDGen gen = new VTDGen();
				if (inputXml == null || inputXml.equals("")) {
					return null;
				}
				byte[] bs;
				try {
					bs = inputXml.getBytes("UTF-8");
				} catch (UnsupportedEncodingException e1) {
					log.error("xml encoding is not utf-8！");
					throw e1;
				}
				try{
				ByteArrayOutputStream bao=new ByteArrayOutputStream();
				gen.setDoc(bs);
		        gen.parse(false);
		        VTDNav vn = gen.getNav();
		          AutoPilot ap = new AutoPilot(vn);
		          XMLModifier xm = new XMLModifier(vn);
	        	  ap.selectXPath(xPath);
	        	  if(ap.evalXPath() == -1){//不存在该节点
	        		  ap.selectXPath(xPath.substring(0, xPath.lastIndexOf("/")));
	        		  if(ap.evalXPath() != -1){
	            		  xm.insertAfterHead("<"+path+"></"+path+">");
	        		  }
	        	  }
		         xm.output(bao);
		         xml = new String(bao.toByteArray(), "UTF-8");
			     bao.close();
			     inputXml = xml;
				}catch(Exception e){
					return "";
				}
	        }
	        return inputXml;
		}
	}
}
